/*
 * Copyright (c) 2009 by Vsevolod Velichko <torkvemada@nigma.ru>
 *
 ***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************
*/

#include "indigo.h"

static std::vector<unsigned int> dingo_free_ids;
static unsigned int dingo_max_id = 0;

/* Dingo object constructor */
PHP_METHOD(PHP_DINGO_CLASS_NAME, __construct)
{
	/* Retrieve class */
	zval* objptr = getThis();
	if (!objptr)
	{
		php_error_docref(NULL TSRMLS_CC, E_ERROR, "constructor cannot be called statically!");
		RETURN_FALSE;
	}

	/* Set ID */
	long id;
	if (dingo_free_ids.size() > 0)
	{
		id = dingo_free_ids.back();
		dingo_free_ids.pop_back();
	}
	else
	{
		id = ++dingo_max_id;		
	}
	if (id == 0)
		php_error_docref(NULL TSRMLS_CC, E_ERROR, "maximum objects number exceeded!");
	zend_update_property_long(Z_OBJCE_P(objptr), objptr, (char*)"ID", 2, id TSRMLS_CC);
	dingoSetSessionId(id);
	dingoReset();
}
/* Dingo object destructor */
PHP_METHOD(PHP_DINGO_CLASS_NAME, __destruct)
{
	/* Retrieve class */
	zval* objptr = getThis();
	if (!objptr)
	{
		php_error_docref(NULL TSRMLS_CC, E_ERROR, "Destructor cannot be called statically!");
		RETURN_FALSE;
	}

	/* Free ID */
	GET_ID(id);
	dingoSetSessionId(id);
	dingoEndLog();
	dingo_free_ids.push_back(id);

}
/* Class string representation */
PHP_METHOD(PHP_DINGO_CLASS_NAME, __toString)
{
	RETURN_STRINGL((char*)PHP_DINGO_CLASS_NAME, sizeof(PHP_DINGO_CLASS_NAME)-1, 1);
}

/**
 * @brief reset settings.
 *
 * @return void
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, reset)
{
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoReset());
	if(result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't reset settings: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/**
 * @brief set Dingo log path.
 * Tries to stop previous log, then starts new log.
 * If empty string is passed, no new log is started.
 *
 * @param string filename - path to the logfile
 * @return void
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setLogPath)
{
	char *path;
	int len;
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"s", &path, &len) == FAILURE)
	{
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	dingoEndLog();
	if(len>0)
		dingoBeginLog(path);
	RETURN_NULL();
}

/** 
 * @brief set if the highlighting should be loaded from file
 * 
 * @param boolean enable - false for disabled, true for enabled
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setLoadHighlighting)
{
	zend_bool enabled;
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"b", &enabled) == FAILURE)
	{
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetLoadHighlighting(enabled));
	if(result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't change highlighting: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief set the input file format
 * 
 * @param string format - can be `smiles`, `rsmiles`, `molfile` or `rxnfile`
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setInputFormat)
{
	char *format;
	int len;
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"s", &format, &len) == FAILURE)
	{
		RETURN_NULL();
	}
	if (
			(strncasecmp("smiles", format, 6) != 0)
			&& (strncasecmp("rsmiles", format, 7) != 0)
			&& (strncasecmp("molfile", format, 7) != 0)
			&& (strncasecmp("rxnfile", format, 7) != 0)
		)
	{
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "unsupported input format: %s", format);
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetInputFormat(format));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't set input format: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief read the molecule for rendering from file
 * 
 * @param string filename - path to the file, containing molecule
 * @return boolean loading result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, loadMolFromFile)
{
	char *path;
	int len;
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"s", &path, &len) == FAILURE)
	{
		RETURN_NULL();
	}
	if (len < 1)
		RETURN_BOOL(0);
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoLoadMolFromFile(path));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't load molecule: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief read the molecule for rendering from string
 * 
 * @param string molecule - molecule to render
 * @return boolean loading result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, loadMolFromString)
{
	char *mol;
	int len;
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"s", &mol, &len) == FAILURE)
	{
		RETURN_NULL();
	}
	if (len < 1)
		RETURN_BOOL(0);
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoLoadMolFromString(mol));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't load molecule: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief read the reaction for rendering from file
 * 
 * @param string filename - path to the file, containing reaction
 * @return boolean loading result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, loadReactionFromFile)
{
	char *path;
	int len;
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"s", &path, &len) == FAILURE)
	{
		RETURN_NULL();
	}
	if (len < 1)
		RETURN_BOOL(0);
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoLoadReactionFromFile(path));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't load reaction: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief read the reaction for rendering from string
 * 
 * @param string reaction - reaction to render
 * @return boolean loading result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, loadReactionFromString)
{
	char *reaction;
	int len;
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"s", &reaction, &len) == FAILURE)
	{
		RETURN_NULL();
	}
	if (len < 1)
		RETURN_BOOL(0);
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoLoadReactionFromString(reaction));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't load reaction: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief set the output file format
 * 
 * @param string format - can be `svg`, `png`, `pdf`. `rxn` or `mol`
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setOutputFormat)
{
	char *format;
	int len;
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"s", &format, &len) == FAILURE)
	{
		RETURN_NULL();
	}
	if (
			(strncasecmp("png", format, 3) != 0)
			&& (strncasecmp("svg", format, 3) != 0)
			&& (strncasecmp("pdf", format, 3) != 0)
			&& (strncasecmp("rxn", format, 3) != 0)
			&& (strncasecmp("mol", format, 3) != 0)
		)
	{
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "unsupported output format: %s", format);
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetOutputFormat(format));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't set output format: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief set resulting image filename
 * 
 * @param string filename - path to the file where to put image
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setOutputFile)
{
	char *filename;
	int len;
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"s", &filename, &len) == FAILURE)
	{
		RETURN_NULL();
	}
	if(len<1)
		RETURN_NULL();
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetOutputFile(filename));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't set output file: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief set the desired image dimensions.
 * If no size is given, the size will be adjusted according to the bond length and margins.
 * 
 * @param int width - width of the image
 * @param int height - height of the image
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setImageSize)
{
	long width, height;
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"ll", &width, &height) == FAILURE)
	{
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetImageSize(width, height));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't set image size: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief set the average bond length in pixels.
 * If size of image is not set, this determines the size of
 * the resulting image, otherwise it's ignored.
 * If neither image size nor bond length was set, bond length
 * is equal to 100 pixels.
 * 
 * @param float bondLength - average bond length
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setBondLength)
{
	double bl;
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"d", &bl) == FAILURE)
	{
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetBondLength(bl));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't set bond length: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief set the margin width ratio to the average bond length.
 * Default is 1.0, and the recommended minimum value is 0.55.
 * 
 * @param float marginFactor - margin width ratio to the average bond length
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setMarginFactor)
{
	double mf;
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"d", &mf) == FAILURE)
	{
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetMarginFactor(mf));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't set margin factor: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief set the thickness of a bond to rt/30
 * of the average bond length. Default is 1.0. 
 * 
 * @param float relativeThickness - thickness of the bond
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setRelativeThickness)
{
	double rt;
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"d", &rt) == FAILURE)
	{
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetRelativeThickness(rt));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't set relative thickness: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief set base foreground color of the image.
 * If not set, color is black.
 * 
 * @param float r - red channel
 * @param float g - green channel
 * @param float b - blue channel
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setBaseColor)
{
	double r, g, b;
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"ddd", &r, &g, &b) == FAILURE)
	{
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetBaseColor(r, g, b));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't set base color: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief set background color of the image.
 * If not set, background color is transparent.
 * 
 * @param float r - red channel
 * @param float g - green channel
 * @param float b - blue channel
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setBackgroundColor)
{
	double r, g, b;
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"ddd", &r, &g, &b) == FAILURE)
	{
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetBackgroundColor(r, g, b));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't set background color: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief set the implicit hydrogen display mode
 * to `none`, `terminal`, `hetero`, `terminalhetero`, or `all`.
 * Default is `terminalhetero`.
 * 
 * @param mode - on of the above
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setImplicitHydrogenMode)
{
	char *mode;
	int len;
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"s", &mode, &len) == FAILURE)
	{
		RETURN_NULL();
	}
	if (
			(strcasecmp("none", mode) != 0)
			&& (strcasecmp("terminal", mode) != 0)
			&& (strcasecmp("hetero", mode) != 0)
			&& (strcasecmp("terminalhetero", mode) != 0)
			&& (strcasecmp("all", mode) != 0)
		)
	{
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "unsupported implicit hydrogen mode: %s", mode);
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetImplicitHydrogenMode(mode));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't set implicit hydrogen mode: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief enable or disable the coloring of atom labels, e.g. nitrogen is blue,
 * oxygen is red etc.
 * 
 * @param boolean enable - false for disabled, true for enabled
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setColoring)
{
	zend_bool enabled;
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"b", &enabled) == FAILURE)
	{
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetColoring(enabled));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't change coloring mode: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief set valences visibility.
 * 
 * @param boolean enable - false for disabled, true for enabled
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setValencesVisible)
{
	zend_bool enabled;
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"b", &enabled) == FAILURE)
	{
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetValencesVisible(enabled));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't change valences visibility: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief set atom IDs visibility.
 * Mostly for debug purposes.
 * 
 * @param boolean enable - false for disabled, true for enabled
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setAtomIdsVisible)
{
	zend_bool enabled;
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"b", &enabled) == FAILURE)
	{
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetAtomIdsVisible(enabled));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't change atom IDs visibility: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief set bond IDs visibility.
 * Mostly for debug purposes.
 * 
 * @param boolean enable - false for disabled, true for enabled
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setBondIdsVisible)
{
	zend_bool enabled;
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"b", &enabled) == FAILURE)
	{
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetBondIdsVisible(enabled));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't change bond IDs visibility: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief set the option to show the “Chiral” flag when appropriate. 
 * 
 * @param boolean enable - false for disabled, true for enabled
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setStereoOldStyle)
{
	zend_bool enabled;
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"b", &enabled) == FAILURE)
	{
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetStereoOldStyle(enabled));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't change stereo display style: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief set the atom label display mode
 * to `normal`, `forceshow` or `forcehide`.
 * Default is `normal`.
 * 
 * @param string mod - label mode
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setLabelMode)
{
	char *mode;
	int len;
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"s", &mode, &len) == FAILURE)
	{
		RETURN_NULL();
	}
	if (
			(strcasecmp("normal", mode) != 0)
			&& (strcasecmp("forceshow", mode) != 0)
			&& (strcasecmp("forcehide", mode) != 0)
		)
	{
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "unsupported label mode: %s", mode);
		RETURN_NULL();
	}
	
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetLabelMode(mode));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't change label mode: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief aromatize or dearomatize the molecule prior to rendering.
 * 
 * @param string mode - mode of aromatization: `aromatize`(`arom`), `dearomatize`(`dear`, `dearom`) or `none`
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setAromatization)
{
	char *mode;
	int len;
	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"s", &mode, &len) == FAILURE)
	{
		RETURN_NULL();
	}
	if (
			(strcasecmp("aromatize", mode) != 0)
			&& (strcasecmp("arom", mode) != 0)
			&& (strcasecmp("dearomatize", mode) != 0)
			&& (strcasecmp("dearom", mode) != 0)
			&& (strcasecmp("dear", mode) != 0)
			&& (strcasecmp("none", mode) != 0)
		)
	{
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "unsupported aromatization mode: %s", mode);
		RETURN_NULL();
	}
	
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetAromatization(mode));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't set aromatization mode: %s", dingoGetLastError());
	RETURN_BOOL(result);

}

/** 
 * @brief enable or disable highlighting with thick lines and bold characters.
 * 
 * @param boolean enable - false for disabled, true for enabled
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setHighlightThicknessEnabled)
{
	zend_bool enabled;
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"b", &enabled) == FAILURE)
	{
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetHighlightThicknessEnabled(enabled));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't set highlight thickness: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief enable or disable highlighting with color.
 * 
 * @param boolean enable - false for disabled, true for enabled. If disabled, other params are ignored
 * @param float r - red channel
 * @param float g - green channel
 * @param float b - blue channel
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setHighlightColor)
{
	zend_bool enabled;
	double r = 0.0, g = 0.0, b = 0.0;
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"b|ddd", &enabled, &r, &g, &b) == FAILURE)
	{
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetHighlightColorEnabled(enabled));
	if (result == 0)
	{
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't change highlight mode: %s", dingoGetLastError());
		RETURN_BOOL(0);
	}
	if (enabled>0)
		result = dingoSetHighlightColor(r, g, b);
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't set highlight color: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief whether or not to force double bond centering when it has two single adjacent bonds, one of which is a stereo (up or down) bond directed towards this double bond. By default disabled.
 * 
 * @param boolean enable - false for disabled, true for enabled.
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setCenterDoubleBondWhenStereoAdjacent)
{
	zend_bool enabled;
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"b", &enabled) == FAILURE)
	{
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetCenterDoubleBondWhenStereoAdjacent(enabled));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't set stereo double bond centering: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief set the color to use for atom-by-atom mapping indices in reactions.
 * 
 * @param float r - red channel
 * @param float g - green channel
 * @param float b - blue channel
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, setAAMColor)
{
	double r, g, b;
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, (char*)"ddd", &r, &g, &b) == FAILURE)
	{
		RETURN_NULL();
	}
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoSetAAMColor(r, g, b));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't set AAM color: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief render the image
 * 
 * @return boolean operation result - true if success
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, render)
{
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoRender());
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't render an image: %s", dingoGetLastError());
	RETURN_BOOL(result);
}

/** 
 * @brief render the image to string
 * 
 * @return string
 */
PHP_METHOD(PHP_DINGO_CLASS_NAME, getRenderedString)
{
	unsigned char *str;
	int str_len;
	GET_ID(id);
	dingoSetSessionId(id);
	zend_bool result(dingoGetResult(&str, &str_len));
	if (result == 0)
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "couldn't get rendered image: %s", dingoGetLastError());
	RETURN_STRINGL((char*)str, str_len, 1);
}

